/*
Author: Surpass-HC
Source: https://gitee.com/hxchuan000/clpc-1
Mail: surpass-hc@qq.com
*/
#include <clptcParse.h>
#include <typecvrt.h>
#include <clptcCmdList.h>
#include <genrand.h>
#include <crcCalc.h>
#include <parmNorm.h>
#include <clptcDlgItf.h>

// 0xb5 for not ascii byte begin
static DEV_C51_CODE_FLAG const uint8_t clptcPsFixedHead[CLPTC_PACK_PST_VERS] = {0XB5, 0XD7}; // fixed head save

static void clptcPsRestart(clptcParseStc *parseIstc)
{
    parseIstc->parseState = clptcPsSt_Begin;
}

clptcLenType clptcPackFinV1(void *ptcPtr)
{
    clptcParseStc *parseIstc = (clptcParseStc *)ptcPtr;
    // clptcSetCrc(parseIstc, calcHashCrc8(clptcGetDataPtrV1(parseIstc), clptcGetDataSizeV1(parseIstc)));
    memcpy(parseIstc->pkptr, clptcPsFixedHead, sizeof(clptcPsFixedHead));
    clptcSetVers(parseIstc, clptcPsVers1);
    return clptcGetPackSizeV1(parseIstc);
}

int clptcInitV1(void *ptcPtr, clptcLenType setPackSize, clptcFunItfStc *cfgDinf)
{
    clptcParseStc *parseIstc = (clptcParseStc *)ptcPtr;
    if (!(parseIstc))
    {
        logerrorc("");
        return -1;
    }
    if ((setPackSize < (clptcPsComSize + clptcGetHeadSizeV1)))
    {
        logerrorc("");
        return -1;
    }
    parseIstc->dFunInf = cfgDinf;
    if (!parseIstc->dFunInf)
    {
        logdebugc("input infor error");
    }
    else
    {
        logdebugc("this dev inf:%d", clptcFunGetType(parseIstc->dFunInf));
    }
    parseIstc->totalsize = setPackSize - clptcPsComSize;
    if (clptcGetDataMaxSizeV1(ptcPtr) < sizeof(clptcCmdUnion_wrtFileSnd))
    {
        logdebugc("clp pack is small:%d<%d", clptcGetDataMaxSizeV1(ptcPtr), (int)sizeof(clptcCmdUnion_wrtFileSnd));
    }
    clptcPsRestart(parseIstc);
    return 0;
}
int clptcPsDataV1(void *ptcPtr, uint8_t indata)
{
    int tmpflag = 0;
    clptcParseStc *parseIstc = (clptcParseStc *)ptcPtr;
    switch (parseIstc->parseState)
    {
    case clptcPsSt_Begin:
    {
        parseIstc->parseState = clptcPsSt_FixHd;
        parseIstc->rcvCount = 0;
        memset(parseIstc->pkptr, 0, parseIstc->totalsize);
    }
    case clptcPsSt_FixHd:
    {
        if (parseIstc->rcvCount < sizeof(clptcPsFixedHead))
        {
            if (indata == clptcPsFixedHead[parseIstc->rcvCount])
            {

                parseIstc->pkptr[parseIstc->rcvCount++] = indata;
            }
            else
            {
                parseIstc->rcvCount = 0;
                if (indata == clptcPsFixedHead[parseIstc->rcvCount])
                {
                    parseIstc->pkptr[parseIstc->rcvCount++] = indata;
                }
            }
        }
        if (parseIstc->rcvCount >= sizeof(clptcPsFixedHead))
        {
            parseIstc->parseState = clptcPsSt_Vers;
        }
        break;
    }
    case clptcPsSt_Vers:
    {
        parseIstc->pkptr[parseIstc->rcvCount++] = indata;
        if (!(clptcGetVers(parseIstc) == clptcPsVers1))
        {
            logerrorc("parseVersion:%d!=%d", clptcPsVers1, clptcGetVers(parseIstc));
            clptcPsRestart(parseIstc);
            break;
        }
        parseIstc->needSize = clptcGetHeadSizeV1;
        if (!parseIstc->needSize)
        {
            logerrorc("parseVersion:%d", clptcGetHeadSizeV1);
            clptcPsRestart(parseIstc);
            break;
        }
        parseIstc->parseState = clptcPsSt_Hd;
        break;
    }
    case clptcPsSt_Hd:
    {
        if (parseIstc->rcvCount < parseIstc->needSize)
        {
            parseIstc->pkptr[parseIstc->rcvCount++] = indata;
        }
        if (parseIstc->rcvCount < parseIstc->needSize)
        {
            break;
        }
        parseIstc->needSize = clptcGetPackSizeV1(parseIstc);
        if (!(parseIstc->needSize >= clptcGetHeadSizeV1 &&
              parseIstc->needSize <= parseIstc->totalsize))
        {
            logerrorc("%u,%u", parseIstc->needSize, parseIstc->totalsize);
            clptcPsRestart(parseIstc);
            break;
        }
        if (parseIstc->needSize > parseIstc->rcvCount)
        {
            parseIstc->parseState = clptcPsSt_Data;
            break;
        }
    }
    case clptcPsSt_Data:
    {
        if (parseIstc->rcvCount < parseIstc->needSize)
        {
            parseIstc->pkptr[parseIstc->rcvCount++] = indata;
        }
        if (parseIstc->rcvCount < parseIstc->needSize)
        {
            break;
        }
        // if (clptcChkCrcV1(parseIstc) < 0)
        // {
        //     logerrorc("");
        //     clptcPsRestart(parseIstc);
        //     break;
        // }
        // parse ok!
        // ptcPsOptCbk(parseIstc);
        tmpflag = 1;
    }
    default:
    {
        clptcPsRestart(parseIstc);
        break;
    }
    }
    return tmpflag;
}
// uint16_t sm4CryptByFlag(CRYPTE_SM4_FLAG_ENUM encflag, uint8_t *inbuf, uint8_t *outbuf, uint16_t csize, const uint8_t *key)
// {
//     if (encflag == CRYPTE_SM4_DEC_FLAG)
//     {
//         return sm4DecrypteMulti(inbuf, outbuf, csize, key);
//     }
//     return sm4EncrypteMulti(inbuf, outbuf, csize, key);
// }
static int clptcDealDecrypte(void *inptr)
{
    clptcParseStc *tmpPtc = (clptcParseStc *)inptr;
    uint16_t tmpvalue;
#if CLPTC_CPL_USE_CRYPTE
    switch (clptcGetCrypteV1(inptr))
    {
    case clptcCrypte_None:
    {
        break;
    }
    case clptcCrypte_Sm4Pre:
    {
        if (!sm4DecryptePro1(clptcGetDataPtrV1(inptr), clptcGetDataSizeV1(inptr), CLPTC_CNT_SM4_PRE_KEY))
        {
            logerrorc("clptcCrypte_Sm4Pre error");
            return -1;
        }
        break;
    }
    case clptcCrypte_Sm4Dlg:
    {

        if (!sm4DecryptePro1(clptcGetDataPtrV1(inptr), clptcGetDataSizeV1(inptr),
                             clptcDlgMgrGetKey(tmpPtc->dFunInf->lpDlgMgr, clptcGetAddrV1(inptr))))
        {
            logerrorc("clptcCrypte_Sm4Dlg error");
            return -1;
        }
        break;
    }
    default:
    {
        logdebugc("not surpport:%d", clptcGetCrypteV1(inptr));
        return -1;
    }
    }
#else
    if (clptcGetCrypteV1(inptr) != clptcCrypte_None)
    {
        logerrorc("key not set:%d", clptcGetCrypteV1(inptr));
        return -1;
    }
#endif
    // check orgin crc.
    tmpvalue = calcSumCrc16(clptcGetDataPtrV1(inptr), clptcGetDataSizeV1(inptr));
    if (tmpvalue != clptcGetCrc1V1(inptr))
    {
        logerrorc("crypte data crc error:%d,%d", tmpvalue, clptcGetCrc1V1(inptr));
        return -1;
    }
    return 0;
}

static int clptcDealEncrypte(void *inptr)
{
#if CLPTC_CPL_USE_CRYPTE
    clptcParseStc *tmpPtc = (clptcParseStc *)inptr;
    clptcLenType tmpsize;

    // calc size
    switch (clptcGetCrypteV1(inptr))
    {
    case clptcCrypte_Sm4Pre:
    case clptcCrypte_Sm4Dlg:
    {
        tmpsize = (clptcGetDataSizeV1(inptr) + SM4_BLOCK_SIZE - 1) / SM4_BLOCK_SIZE * SM4_BLOCK_SIZE;
        if (tmpsize > clptcGetDataMaxSizeV1(inptr))
        {
            logerrorc("resp size error:%d, %d", clptcGetCrypteV1(inptr), tmpsize);
            return -1;
        }
        clptcSetCrc1V1(inptr, calcSumCrc16(clptcGetDataPtrV1(inptr), tmpsize));
        clptcSetDataSizeV1(inptr, tmpsize);
        break;
    }
    default:
    {
        clptcSetCrc1V1(inptr, calcSumCrc16(clptcGetDataPtrV1(inptr), clptcGetDataSizeV1(inptr)));
        clptcSetCrypteV1(inptr, clptcCrypte_None);
        return 0;
    }
    }

    switch (clptcGetCrypteV1(inptr))
    {
    case clptcCrypte_Sm4Pre:
    {
        if (!sm4EncryptePro1(clptcGetDataPtrV1(inptr), clptcGetDataSizeV1(inptr), CLPTC_CNT_SM4_PRE_KEY))
        {
            logerrorc("clptcCrypte_Sm4Pre error");
            return -1;
        }
        break;
    }
    case clptcCrypte_Sm4Dlg:
    {
        if (!sm4EncryptePro1(clptcGetDataPtrV1(inptr), clptcGetDataSizeV1(inptr),
                             clptcDlgMgrGetKey(tmpPtc->dFunInf->lpDlgMgr, clptcGetAddrV1(inptr))))
        {
            logerrorc("clptcCrypte_Sm4Pre error");
            return -1;
        }
        break;
    }
    }
#else
    clptcSetCrc1V1(inptr, calcSumCrc16(clptcGetDataPtrV1(inptr), clptcGetDataSizeV1(inptr)));
    clptcSetCrypteV1(inptr, clptcCrypte_None);
#endif
    return 0;
}

#if CLPTC_CPL_SUB_ENABLE
static int clptcSubDealCmd(void *inptr)
{
    clptcParseStc *tmpPtc = (clptcParseStc *)inptr;
    uint8_t tmpvalue;
    tmpvalue = clptcGetAddrV1(inptr);
    if (tmpvalue == CLPTC_SLAVE_BOARDCAST_ADDR)
    {
        // check boardcast
        switch (clptcGetCmdIdV1(inptr))
        {
        case clptcCmdEnumDef(rwSn):
        case clptcCmdEnumDef(rwAddr):
        {
            break;
        }
        default:
        {
            logdebugc("cmd not surpport boardcast:%d", clptcGetCmdIdV1(inptr));
            return -1;
        }
        }
    }
    else
    {
        // check addr
        if (tmpvalue != parmNormIstc.addr485)
        {
            // logdebugc("");
            return -1;
        }
    }

    // decrypte receive data!
    if (clptcDealDecrypte(inptr) < 0)
    {
        logerrorc("sub decrypte error");
        tmpPtc->subRespStatFlag = clptcCmdResp_DecryptErr;
        goto cmdSndBackPtr;
    }

    // logdebugc("");
    tmpPtc->subRespSizeHostCmdId = 0;
    tmpPtc->subRespStatFlag = clptcCmdResp_OK;
    tmpPtc->subRespCrypteHostAddr = clptcGetCrypteV1(inptr);

    if (clptcCmdSubDealByID(inptr) < 0)
    {
        logerrorc("sub do cmd not surpport:%d", clptcGetCmdIdV1(inptr));
        tmpPtc->subRespStatFlag = clptcCmdResp_NotSupport;
        goto cmdSndBackPtr;
    }

    if (tmpPtc->subRespStatFlag != clptcCmdResp_OK)
    {
        logerrorc("cmd result flag error:%d,%d", tmpPtc->subRespStatFlag, clptcGetCmdIdV1(inptr));
        goto cmdSndBackPtr;
    }
    if (tmpPtc->subRespSizeHostCmdId > clptcGetDataMaxSizeV1(inptr))
    {
        logerrorc("response size big error:%d,%d", tmpPtc->subRespStatFlag, clptcGetCmdIdV1(inptr));
        tmpPtc->subRespStatFlag = clptcCmdResp_EncryptErr;
        goto cmdSndBackPtr;
    }
    clptcSetCrypteV1(inptr, tmpPtc->subRespCrypteHostAddr);
    clptcSetResponseV1(inptr, tmpPtc->subRespStatFlag);
    clptcSetDataSizeV1(inptr, tmpPtc->subRespSizeHostCmdId);

    // encrypte receive data!
    if (clptcDealEncrypte(inptr) < 0)
    {
        logerrorc("sub clptcDealEncrypte error");
        tmpPtc->subRespStatFlag = clptcCmdResp_EncryptErr;
        goto cmdSndBackPtr;
    }

cmdSndBackPtr:
    if (tmpPtc->subRespStatFlag == clptcCmdResp_NotSndBack)
    {
        logdebugc("cmd:%d, not need response", clptcGetCmdIdV1(inptr));
        return 0;
    }
    else if (tmpPtc->subRespStatFlag != clptcCmdResp_OK)
    {
        clptcSetCrypteV1(inptr, clptcCrypte_None);
        clptcSetResponseV1(inptr, tmpPtc->subRespStatFlag);
        clptcSetDataSizeV1(inptr, 0);
        clptcSetCrc1V1(inptr, calcSumCrc16(clptcGetDataPtrV1(inptr), clptcGetDataSizeV1(inptr)));
        clptcPackFinV1(inptr);
    }
    else
    {
        // clptcSetDataSizeV1(inptr, tmpPtc->subRespSizeHostCmdId);
        clptcPackFinV1(inptr);
    }
    return clptcGetPackSizeV1(inptr);
}
int clptcSubPsAndDeal(void *inptr, char indata)
{
    clptcParseStc *tmpopt = (clptcParseStc *)inptr;
    if (clptcPsDataV1(inptr, indata) <= 0)
    {
        return 0;
    }
    // logdebugc("");
    return clptcSubDealCmd(tmpopt);
}
#endif

#if CLPTC_CPL_HOST_ENABLE
int clptcHostPackCommV1(void *inptr, uint8_t saddr, uint8_t scrypte, uint16_t cmdid, clptcLenType cmdlen)
{
    clptcParseStc *tmpPtc = (clptcParseStc *)inptr;
    tmpPtc->subRespCrypteHostAddr = saddr; // save cmd addr
    tmpPtc->subRespSizeHostCmdId = cmdid;  // save cmd id
    clptcSetAddrV1(inptr, saddr);
    clptcSetCmdIdV1(inptr, cmdid);
    clptcSetDataSizeV1(inptr, cmdlen);
    clptcSetResponseV1(inptr, 0);
    // set crypte
    clptcSetCrypteV1(inptr, scrypte);
    if (clptcDealEncrypte(inptr) < 0)
    {
        logerrorc("host clptcDealEncrypte error");
        return 0;
    }
    // pack and parse use same buffer, so new cmd and restart parse.
    clptcPsRestart(tmpPtc);

    return clptcPackFinV1(inptr);
}
// return : <0 parse error, 0:cmdid,addr not pair, 1:ok, >1 response error clptcCmdRespEnum
static int clptcHostDoResp(void *inptr)
{
    clptcParseStc *tmpPtc = (clptcParseStc *)inptr;
    int tmpflag = 1;

    // decrypte receive data!
    if (clptcDealDecrypte(inptr) < 0)
    {
        logerrorc("host decrypte error");
        tmpflag = -7; // wait?
        goto hostDoRespErrPtr;
    }

    // response error
    if (clptcGetResponseV1(tmpPtc) > 0)
    {
        tmpflag = clptcGetResponseV1(tmpPtc);
        goto hostDoRespErrPtr;
    }

    tmpflag = clptcCmdHostDealByID(tmpPtc);

hostDoRespErrPtr:
    if (tmpPtc->subRespSizeHostCmdId != clptcGetCmdIdV1(tmpPtc))
    {
        logwarnc("diff cmd id:%d,%d", tmpPtc->subRespSizeHostCmdId, clptcGetCmdIdV1(tmpPtc));
        tmpflag = 0;
    }
    else if (tmpPtc->subRespCrypteHostAddr != clptcGetAddrV1(tmpPtc))
    {
        logwarnc("diff cmd addr:%d,%d", tmpPtc->subRespCrypteHostAddr, clptcGetAddrV1(tmpPtc));
        tmpflag = 0;
    }
    return tmpflag;
}
// return:
// 0:wait response, 1:ok, <0:error, >1 response error clptcCmdRespEnum
int clptcHostPsAndDeal(void *inptr, char indata)
{
    clptcParseStc *tmpopt = (clptcParseStc *)inptr;
    if (clptcPsDataV1(inptr, indata) <= 0)
    {
        return 0;
    }
    return clptcHostDoResp(tmpopt);
}
#endif
